package com.weilx959.spt.core.component;

import com.jfoenix.controls.JFXRadioButton;
import com.weilx959.spt.core.constants.CommonConstants;
import com.weilx959.spt.core.utils.FXMLUtil;
import com.weilx959.spt.core.utils.Try;
import javafx.beans.DefaultProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.util.Pair;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;

import java.util.Objects;
import java.util.Optional;

/**
 * 单选按钮表单项控件
 *
 * @author weilx
 * @since 2024年6月2日
 */
@DefaultProperty("options")
public class RadioButtonFormItemControl<T> extends ParentFormItemControl<T> {
    private static final String XML = "/assets/fxml/component/radio-button-form-control.fxml";

    @FXML
    private Label labelControl;

    @FXML
    private HBox radioButtonBox;

    private ToggleGroup toggleGroup;

    private ObjectProperty<ObservableList<Pair<String, T>>> options;

    @Getter
    @Setter
    private boolean required;

    public RadioButtonFormItemControl() {
        super();
        initialize();
    }

    @Override
    public Try<String> validate() {
        return Try.of(() -> {
            if (required && Objects.isNull(getValue())) {
                throw new IllegalArgumentException(getLabel() + "不能为空！");
            }
        });
    }

    public ObjectProperty<ObservableList<Pair<String, T>>> optionsProperty() {
        if (Objects.isNull(options)) {
            options = new SimpleObjectProperty<>(this, "OPTIONS", FXCollections.observableArrayList());
        }
        return options;
    }

    public void setOptions(ObservableList<Pair<String, T>> options) {
        optionsProperty().set(options);
    }

    public ObservableList<Pair<String, T>> getOptions() {
        return optionsProperty().get();
    }

    private void initialize() {
        // load
        FXMLUtil.loadControl(this, XML);
        // radio button
        toggleGroup = new ToggleGroup();
        this.loadRadioButtons(getOptions());
        // value listener
        toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) ->
            Optional.ofNullable(newValue).map(Toggle::getUserData).map(v -> (T) v).ifPresent(this::setValue));
        valueProperty().addListener((observable, oldValue, newValue) -> {
            JFXRadioButton toggleButton = radioButtonBox.getChildren()
                .stream()
                .map(JFXRadioButton.class::cast)
                .filter(rb -> rb.getUserData().equals(newValue))
                .findFirst()
                .orElse(null);
            toggleGroup.selectToggle(toggleButton);
        });
        // options listener
        optionsProperty().addListener((observable, oldValue, newValue) -> this.loadRadioButtons(newValue));
        getOptions().addListener((ListChangeListener<Pair<String, T>>) c -> this.loadRadioButtons(getOptions()));
    }

    /**
     * 加载单选按钮
     *
     * @param options 选项
     */
    private void loadRadioButtons(ObservableList<Pair<String, T>> options) {
        radioButtonBox.getChildren().clear();
        if (CollectionUtils.isEmpty(options)) {
            return;
        }
        for (Pair<String, T> option : options) {
            JFXRadioButton radioButton = new JFXRadioButton(option.getKey());
            radioButton.setUserData(option.getValue());
            radioButton.setToggleGroup(toggleGroup);
            radioButton.getStyleClass().add(CommonConstants.Style.RADIO_BUTTON);
            radioButtonBox.getChildren().add(radioButton);
        }
        JFXRadioButton toggleButton = radioButtonBox.getChildren()
            .stream()
            .map(JFXRadioButton.class::cast)
            .filter(rb -> rb.getUserData().equals(getValue()))
            .findFirst()
            .orElse(null);
        toggleGroup.selectToggle(toggleButton);
    }

    // builder
    public static <T> RadioButtonFormItemControlBuilder<T> builder() {
        return new RadioButtonFormItemControlBuilder<>();
    }

    public static class RadioButtonFormItemControlBuilder<T>
        extends ParentFormItemControlBuilder<T, RadioButtonFormItemControl<T>, RadioButtonFormItemControlBuilder<T>> {
        private ObservableList<Pair<String, T>> options;
        private boolean required;

        public RadioButtonFormItemControlBuilder<T> options(ObservableList<Pair<String, T>> options) {
            this.options = options;
            return this;
        }

        public RadioButtonFormItemControlBuilder<T> option(Pair<String, T> option) {
            if (Objects.isNull(options)) {
                options = FXCollections.observableArrayList();
            }
            options.add(option);
            return this;
        }

        public RadioButtonFormItemControlBuilder<T> option(String key, T value) {
            return option(new Pair<>(key, value));
        }

        public RadioButtonFormItemControlBuilder<T> required(boolean required) {
            this.required = required;
            return this;
        }

        @Override
        public RadioButtonFormItemControl<T> build() {
            RadioButtonFormItemControl<T> control = super.build();
            control.setRequired(required);
            if (CollectionUtils.isNotEmpty(options)) {
                control.setOptions(options);
            }
            return control;
        }

        @Override
        protected RadioButtonFormItemControl<T> newFormItemControl() {
            return new RadioButtonFormItemControl<>();
        }

        @Override
        protected RadioButtonFormItemControlBuilder<T> self() {
            return this;
        }
    }
}
